package com.kreedx.article.factory.config;

import com.gargoylesoftware.htmlunit.*;
import com.gargoylesoftware.htmlunit.html.HtmlPage;
import com.gargoylesoftware.htmlunit.javascript.JavaScriptErrorListener;
import com.gargoylesoftware.htmlunit.util.NameValuePair;
import com.geccocrawler.gecco.downloader.AbstractDownloader;
import com.geccocrawler.gecco.downloader.DownloadException;
import com.geccocrawler.gecco.downloader.UserAgent;
import com.geccocrawler.gecco.request.HttpPostRequest;
import com.geccocrawler.gecco.request.HttpRequest;
import com.geccocrawler.gecco.response.HttpResponse;
import com.geccocrawler.gecco.spider.SpiderThreadLocal;
import com.geccocrawler.gecco.utils.UrlUtils;
import org.apache.http.HttpHost;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * 利用htmlunit实现js的dom操作和ajax
 * 
 * @author huchengyi
 *
 */
@com.geccocrawler.gecco.annotation.Downloader("htmlUnitDownloder")
public class HtmlUnitDownloder extends AbstractDownloader {

	private final WebClient webClient;
	
	public HtmlUnitDownloder() {
		this.webClient = new WebClient(BrowserVersion.CHROME);
		this.webClient.getOptions().setThrowExceptionOnFailingStatusCode(false);
		this.webClient.getOptions().setThrowExceptionOnScriptError(false);
		this.webClient.getOptions().setRedirectEnabled(false);
		this.webClient.getOptions().setCssEnabled(false);
		this.webClient.setJavaScriptTimeout(1000);
		this.webClient.setJavaScriptErrorListener(new JavaScriptErrorListener() {
			@Override
			public void scriptException(HtmlPage htmlPage, ScriptException e) {

			}

			@Override
			public void timeoutError(HtmlPage htmlPage, long l, long l1) {

			}

			@Override
			public void malformedScriptURL(HtmlPage htmlPage, String s, MalformedURLException e) {

			}

			@Override
			public void loadScriptError(HtmlPage htmlPage, URL url, Exception e) {

			}

			@Override
			public void warn(String s, String s1, int i, String s2, int i1) {

			}
		});
	}
	
	public HttpResponse download(HttpRequest request, int timeout) throws DownloadException {
		try {
			URL url = new URL(request.getUrl());
			WebRequest webRequest = new WebRequest(url);
			webRequest.setHttpMethod(HttpMethod.GET);
			if(request instanceof HttpPostRequest) {//post
				HttpPostRequest post = (HttpPostRequest)request;
				webRequest.setHttpMethod(HttpMethod.POST);
				List<NameValuePair> requestParameters = new ArrayList<NameValuePair>();
				for(Map.Entry<String, String> entry : post.getFields().entrySet()) {
					NameValuePair nvp = new NameValuePair(entry.getKey(), entry.getValue().toString());
					requestParameters.add(nvp);
				}
				webRequest.setRequestParameters(requestParameters);	
			}
			//header
			boolean isMobile = SpiderThreadLocal.get().getEngine().isMobile();
			webRequest.setAdditionalHeader("User-Agent", UserAgent.getUserAgent(isMobile));
			webRequest.setAdditionalHeaders(request.getHeaders());
			//proxy
			HttpHost proxy = SpiderThreadLocal.get().getEngine().getProxysLoader().getProxy();
			if(proxy != null) {
				webRequest.setProxyHost(proxy.getHostName());
				webRequest.setProxyPort(proxy.getPort());
			}
			//timeout
			this.webClient.getOptions().setTimeout(timeout);
			//request,response
			webClient.getPage(webRequest);
			HtmlPage page = webClient.getPage(request.getUrl());
			HttpResponse resp = new HttpResponse();
			WebResponse webResponse = page.getWebResponse();
			int status = webResponse.getStatusCode();
			resp.setStatus(status);
			if(status == 302 || status == 301) {
				String redirectUrl = webResponse.getResponseHeaderValue("Location");
				resp.setContent(UrlUtils.relative2Absolute(request.getUrl(), redirectUrl));
			} else if(status == 200) {
				String content = page.asXml();
				resp.setContent(content);
				InputStream inputStream = webResponse.getContentAsStream();
				ByteArrayOutputStream baos = new ByteArrayOutputStream();
				byte[] buffer = new byte[1024];
				int len;
				byte[] dataBytes;
				while ((len = inputStream.read(buffer)) != -1 ) {
					baos.write(buffer, 0, len);
				}
				baos.flush();
				dataBytes = baos.toByteArray();
				resp.setRaw(new ByteArrayInputStream(dataBytes));
				String contentType = webResponse.getContentType();
				resp.setContentType(contentType);
				String charset = getCharset(request.getCharset(), contentType);
				resp.setCharset(charset);
			} else {
				throw new DownloadException("ERROR : " + status);
			}
			return resp;
		} catch(Exception ex) {
			throw new DownloadException(ex);
		}
	}

	public void shutdown() {
		webClient.close();
	}
}
